/*
 * Ethan Levine
 * 
 * The contents of this file are subject to the GNU General Public
 * License (GPL) v2.  The full contents of this license can be
 * found at Mathma's website, http://www.codeplex.com/mathma/license
 */

package mathma.core.expr;

import mathma.core.form.UnknownSuspensionException;

import mathma.core.value.Value;
import mathma.core.value.ValueType;

/**
 * <p>A symbolic expression.  An Expression object represents any symbolic expression,
 * including but not limited to:
 * <ul>
 * <li>Values (Real, imaginary, matricies, etc...)</li>
 * <li>Variables (including constants)</li>
 * <li>Functions</li>
 * </ul>
 * E represents the backing value for an Expression.  For example, the
 * Expression for the constant PI is backed by the class Real, which is what
 * the constant represents.</p>
 *
 * <p>The backing value type of an Expression can be obtained using the
 * {@link #getType()} method.</p>
 *
 * <p>Any classes that extend Expression must pass the ValueType of the
 * Expression up to the constructor for Expression.</p>
 *
 * @since 0.1.0
 * @author Ethan Levine
 */
public abstract class Expression<E extends Value> {

    private final ValueType<E> type;

    private final ExpressionType expressionType;

    protected Expression(ValueType<E> type, ExpressionType eType) {
        this.type = type;
        expressionType = eType;
    }

    /**
     * Evaluates this Expression.  Note that not all expressions have values.
     * Non-constant variables and certain suspended expressions do not have
     * return values and thus cannot be directly evaluate.  If this is the case,
     * an UnknownValueException is thrown.
     *
     * @return the evaluated value of this Expression.
     * 
     * @throws UnknownValueExpression if this expression does not have a known
     * value.
     */
    abstract public E evaluate();

    /**
     * Applies a variable mapping to this Expression.  Note that this should
     * flow through suspended expressions without calculating them.
     * 
     * @param mapping the variable mapping to apply.
     *
     * @return the expression that results from the mapping.
     *
     * @see mathma.core.expr.VariableMapping
     */
    //abstract public Expression<E> applyMapping(VariableMapping<E> mapping);

    /**
     * Simplifies this Expression using the simplifier provided by the backing
     * value type.  More information about the simplification framework is
     * provided in the mathma.core.simp package.
     *
     * @return the simplified Expression.
     */
    public Expression<E> simplify() {
        return getType().getSimplifier().simplify(this);
    }

    /**
     * Differentiates this Expression by a given variable.  The resulting
     * Expression can be expressed mathematically as d(this)/d(var).  This
     * method should be overridden if at all possible.  However, if this method
     * does not make sense in an expression, failing to override it will cause
     * calling it to throw an UnknownSuspensionException.
     *
     * @param var the variable to differentiate by.
     *
     * @return the derivative of this expression.
     * 
     * @throws mathma.core.form.UnknownSuspensionException if this expression
     * cannot be differentiated.
     */
    public Expression<E> differentiate(Variable<E> var)
            throws UnknownSuspensionException {
        throw new UnknownSuspensionException(
                "Expression.differentiate(var) not overriden.");
    }

    final public ValueType<E> getType() {
        return type;
    }

    final public ExpressionType getExpressionType() {
        return expressionType;
    }
}
